import { ContainerModule, interfaces } from 'inversify';
import bindingInterfaces from 'inversify-binding-decorators/dts/interfaces/interfaces';
import { Metadata_Key } from '../constants';
// import { CustomError } from 'ts-custom-error';
// import { ConstantOption } from '../annotation/constant';

/**
 *
 * @param registry
 */
export function autoBind(registry?: interfaces.ContainerModuleCallBack): interfaces.ContainerModule {
  return new ContainerModule((bind, unbind, isBound, rebind) => {
    //
    const provideMetadata: bindingInterfaces.ProvideSyntax[] = Reflect.getMetadata(Metadata_Key.provide, Reflect) || [];
    provideMetadata.map(metadata => resolve(metadata, bind, rebind));
    //
    Reflect.defineMetadata(Metadata_Key.provide, [], Reflect);

    // const constantMetadata: ConstantOption[] = Reflect.getMetadata(METADATA_KEY.constantValue, Reflect) || [];
    // constantMetadata.map(metadata => resolveConstant(metadata, bind, rebind));
    // Reflect.defineMetadata(METADATA_KEY.constantValue, [], Reflect);

    if (registry) {
      registry(bind, unbind, isBound, rebind);
    }
  });
}

function resolve(metadata: bindingInterfaces.ProvideSyntax, bind: interfaces.Bind, rebind: interfaces.Rebind): void {
  const isRebind: boolean = Reflect.getOwnMetadata(Metadata_Key.rebind, metadata.implementationType);
  const id = Reflect.getOwnMetadata(Metadata_Key.toService, metadata.implementationType);

  const bindWrapper = (serviceIdentifier: interfaces.ServiceIdentifier<any>) => {
    if (id && id !== serviceIdentifier) {
      bind(serviceIdentifier).toService(id);
      throw new Error("#111");
    }
    if (isRebind) {
      return rebind(serviceIdentifier);
    }
    return bind(serviceIdentifier);
  };
  try {
    metadata.constraint(bindWrapper, metadata.implementationType);
  } catch (error) {
    if (error instanceof Error) {
      return;
    }
    throw error;
  }
}

// function resolveConstant(metadata: ConstantOption, bind: inversifyInterfaces.Bind, rebind: inversifyInterfaces.Rebind): void {
//   const ids = Array.isArray(metadata.id) ? metadata.id : [metadata.id];
//   const id = ids.shift();
//   if (metadata.rebind) {
//     rebind(id!).toConstantValue(metadata.constantValue);
//   } else {
//     bind(id!).toConstantValue(metadata.constantValue);
//   }
//   for (const item of ids) {
//     bind(item!).toService(id!);
//   }
// }
